<template>
    <div :class="['virtualTable table-wrapper',{'hasFootTable':hasFooter}]" ref="tableWrapper">
        <div class="header-wrapper" ref="headerWrapper" :style="{ width: `calc(100% - ${scrollbarWidth }px)` }">           
            <div class="header">
                <div :class="['checkbox','fixed-left']" v-if="isCheckBox">
                    <customCheckbox :indeterminate="isIndeterminate" :checked="isAllSelected" @change="toggleAllSelection">
                    </customCheckbox>
                </div>
                <div v-for="item in tableColumns" :class="['cell', {
                    'fixed-left': item.fixed === 'left',
                    'fixed-right': item.fixed === 'right'
                }]" :style="{
                    'width': item.computedWidth + 'px', left: item.fixed === 'left' ? leftMap.get(item.key) + 'px' : undefined,
                    right: item.fixed === 'right' ? rightMap.get(item.key) + 'px' : undefined
                }">                    
                    <slot :name="`header-${item.key}`">{{ item.name }}</slot>
                </div>
            </div>
        </div>
        <div class="body-wrapper" ref="bodyWrapper" @scroll="handleScroll">
            <div class="virtual-list-phantom" :style="{ height: totalHeight + 'px', width: tableWidth + 'px' }"></div>
            <div class="virtual-list" :style="{ transform: `translate3d(0, ${offset}px, 0)` }">
                <div v-for="(rowData, index) in visibleData" :key="index" class="table-row">                    
                    <div class="row" @click="handleRowClick(index)" :class="{ 'selected-row': isSelected(index) }">
                        <div :class="['checkbox','fixed-left']" v-if="isCheckBox">
                            <customCheckbox :checked="isChecked(index)" @change="toggleSelection(index)"></customCheckbox>
                        </div>
                        <span v-for="(item,colIndex) in tableColumns" :class="['cell', {
                            'fixed-left': item.fixed === 'left',
                            'fixed-right': item.fixed === 'right'
                        }]" :style="{
                            width: item.computedWidth + 'px', left: item.fixed === 'left' ? leftMap.get(item.key) + 'px' : undefined,
                            right: item.fixed === 'right' ? rightMap.get(item.key) + 'px' : undefined
                        }" @mouseenter="handleCellEnter($event, (state.visibleStartIndex + index) + '-' + colIndex) "@mouseleave="handleCellLeave((state.visibleStartIndex + index) + '-' + colIndex)">
                            <slot :name="`column-${item.key}`" :rowData="rowData">
                                <span>{{ rowData[item.key] }}</span>
                                <Teleport to="body" v-if="tooltipState[(state.visibleStartIndex + index) + '-' + colIndex]?.isShow || tooltipState[(state.visibleStartIndex + index) + '-' + colIndex]?.isTips">
                                    <div class="tool-tips"
                                        :style="{ left: tooltipState[(state.visibleStartIndex + index) + '-' + colIndex]?.position.x + 'px', bottom: tooltipState[(state.visibleStartIndex + index) + '-' + colIndex]?.position.y + 'px', maxWidth: (item.computedWidth?item.computedWidth + 24 : 0) + 'px' }"
                                        @mouseenter="handleTipsEnter((state.visibleStartIndex + index) + '-' + colIndex)"
                                        @mouseleave="handleTipsLeave((state.visibleStartIndex + index) + '-' + colIndex)"
                                    >
                                        {{ rowData[item.key] }}
                                        <div class="tooltip-arrow"></div>
                                    </div>
                                </Teleport>
                            </slot>
                        </span>
                    </div>
                </div>
            </div>
        </div>
        <div class="footer-wrapper" v-if="hasFooter">
            <div class="table-row">
                <div class="row">
                    <div :class="['checkbox','fixed-left']" v-if="isCheckBox">
                        <!-- 合计行不需要复选框 -->
                    </div>
                    <span v-for="(sum, index) in totalData" :class="['cell', {
                        'fixed-left': tableColumns[index].fixed === 'left',
                        'fixed-right': tableColumns[index].fixed === 'right'
                    }]" :style="{
                        width: tableColumns[index].computedWidth + 'px', left: tableColumns[index].fixed === 'left' ? leftMap.get(tableColumns[index].key) + 'px' : undefined,
                        right: tableColumns[index].fixed === 'right' ? rightMap.get(tableColumns[index].key) + 'px' : undefined
                    }">
                        <slot :name="`footer-${tableColumns[index].key}`">{{ sum }}</slot>
                    </span>
                </div>
            </div>
        </div>
        <div class="fixed-box-left" :style="{height: '100%' , width: leftFixedWidthSum + 'px',opacity: (scrollState.isAtLeft ||leftFixedWidthSum<=0)? 0 : 1}"></div>
        <div class="fixed-box-right" :style="{height: '100%', width: rightFixedWidthSum + scrollbarWidth +'px',opacity: (scrollState.isAtRight || rightFixedWidthSum<=0)? 0 : 1}"></div>
    </div>
</template>

<script setup lang="ts">
import customCheckbox from '@/components/CustomCheckbox/index.vue';
import type { TableColumns, RowData} from '@/components/VirtualTable/types';
//滚动事件节流处理
import { throttle } from 'lodash-es';

interface VirtualTableProps {
    tableColumns: TableColumns[];
    data: Record<string, any>[];
    isCheckBox?: boolean;
    hasFooter?: boolean;
};

const props = withDefaults(defineProps<VirtualTableProps>(), {
    isCheckBox: false,
    hasFooter: false,
});
const tableWrapper = ref<HTMLElement | null>();
const headerWrapper = ref<HTMLElement | null>();
const bodyWrapper = ref<HTMLElement | null>();
const selectRowIndex = ref<number>(-1);
function handleRowClick(index: number) {
    const actualIndex = state.visibleStartIndex + index;
    selectRowIndex.value = actualIndex;    
}
function isSelected(index: number) {
    const actualIndex = state.visibleStartIndex + index;
    return selectRowIndex.value === actualIndex;
}


//***************虚拟滚动************
const itemHeight = 40;
const bufferSize = 2;
// const itemWidth = computed(() => columns.value.length * 200);

const state = reactive({
    visibleStartIndex: 0,  // 可见数据起始索引
    visibleEndIndex: 20,   // 可见数据结束索引
});


// 总高度计算
const totalHeight = computed(() => {
    return 1 + props.data.length * itemHeight;
});

// 可见数据计算
const visibleData = computed(() =>
    props.data.slice(state.visibleStartIndex, state.visibleEndIndex) || []
);

let lastScrollTop = 0;
let lastScrollLeft = 0;

const calcScrollbarWidth = () => {
    if (bodyWrapper.value) {
        scrollbarWidth.value = bodyWrapper.value.offsetWidth - bodyWrapper.value.clientWidth;
        calculateColumnWidths(scrollbarWidth.value);
    }
    //判断是否有横向滚动条
    if (bodyWrapper.value && bodyWrapper.value.scrollWidth > bodyWrapper.value.clientWidth) {
        if(scrollState.isAtLeft && scrollState.isAtRight){
            scrollState.isAtLeft = true;
            scrollState.isAtRight = false;
        }
    } else {
        scrollState.isAtLeft = true;
        scrollState.isAtRight = true;
    }
};
//动态计算列的宽度
const tableWidth = ref<number>(0);
const calculateColumnWidths = (val: number) => {

    if (!tableWrapper.value) return;
    const containerWidth = tableWrapper.value.clientWidth - val - (props.isCheckBox ? 30+1 : 1);
    const totalMinWidth = props.tableColumns.reduce((sum, col) => sum + col.minWidth, 0);
    if (containerWidth > totalMinWidth) {
        const remainingSpace = containerWidth - totalMinWidth;
        // 按各列minWidth的比例分配剩余空间
        props.tableColumns.forEach(col => {
            const minWidth = col.minWidth;
            const ratio = minWidth / totalMinWidth;
            const addedWidth = remainingSpace * ratio;
            col.computedWidth = minWidth + addedWidth;
        });
    } else {
        // 使用最小宽度
        props.tableColumns.forEach(col => {
            col.computedWidth = col.minWidth;
        });
    }

    // 更新表格总宽度
    tableWidth.value = containerWidth > totalMinWidth ? containerWidth : totalMinWidth;
    //预计算列的偏移量
    precomputeOffsets();
    //计算冻结总宽度
    calculateFixedWidths();
};

const handleScroll = throttle((event: Event) => {
    // 重置所有tooltip
    tooltipState.value = {};

    const target = event.target as HTMLElement;
    const { scrollTop, scrollLeft } = target;

    if (lastScrollTop !== scrollTop && bodyWrapper.value) {
        // 计算当前滚动位置的索引
        const currentStartIndex = Math.floor(scrollTop / itemHeight);
        const visibleItemCount = Math.ceil(bodyWrapper.value.clientHeight / itemHeight);

        // 更新可见数据起始和结束索引
        state.visibleStartIndex = Math.max(0, currentStartIndex - bufferSize);
        state.visibleEndIndex = Math.min(props.data.length, currentStartIndex + visibleItemCount + bufferSize);
        //记录上一次的滚动条y轴位置 
        lastScrollTop = scrollTop;
    }
    if(lastScrollLeft !== scrollLeft && headerWrapper.value){
        // 新增水平位置判断
        checkScrollPosition(target);
        //body和header横向滚动同步    
        headerWrapper.value.scrollLeft = scrollLeft;
        //记录上一次的滚动条y轴位置 
        lastScrollLeft = scrollLeft;
    }
}, 16, { leading: true, trailing: true });

// 偏移量计算
const offset = computed(() => {
    return state.visibleStartIndex * itemHeight;
});

//-------复选框状态----------
const selectedMap = ref<Set<number>>(new Set()); // 存储数据唯一ID
// 判断选中状态
function isChecked(index: number): boolean {
    const actualIndex = state.visibleStartIndex + index;
    return selectedMap.value.has(actualIndex);

}
// 单行切换
function toggleSelection(index: number) {
    const actualIndex = state.visibleStartIndex + index;
    selectedMap.value.has(actualIndex) ? 
        selectedMap.value.delete(actualIndex) : 
        selectedMap.value.add(actualIndex);
}

// 全选状态计算
const isAllSelected = computed(() => {
    if (props.data.length === 0) return false;
    return props.data.every((_, index) => 
        selectedMap.value.has(index)
    );
});
// 半选中状态
const isIndeterminate = computed(() => {
    if (props.data.length === 0) return false;
    return selectedMap.value.size > 0 && selectedMap.value.size < props.data.length;
});
// 全选操作
function toggleAllSelection() {
    if (props.data.length === 0) return false;
    const allKeys = new Set(props.data.map((_, index) => index));
    
    if (isAllSelected.value) {
        selectedMap.value.clear();
    } else {
        allKeys.forEach(key => selectedMap.value.add(key));
    }
}
//冻结列处理
// 预计算固定列偏移量
const leftMap= new Map<string, number>();
const rightMap= new Map<string, number>();
const precomputeOffsets = () => {
        const fixedLeftColumns = props.tableColumns.filter(c => c.fixed === 'left' && c.key && c.computedWidth);
        const fixedRightColumns = props.tableColumns.filter(c => c.fixed === 'right' && c.key && c.computedWidth);
        let accumulator = props.isCheckBox ? 30 : 0;
        fixedLeftColumns.forEach(col => {
            leftMap.set(col.key!, accumulator);
            accumulator += (col.computedWidth?col.computedWidth:0);
        });        
        accumulator = 0;
        [...fixedRightColumns].reverse().forEach(col => {
            rightMap.set(col.key!, accumulator );
            accumulator += (col.computedWidth?col.computedWidth:0);
        });        
};
//计算冻结列的总宽度，初始化的时候计算
const leftFixedWidthSum = ref(0);
const rightFixedWidthSum = ref(0);
const calculateFixedWidths = () => {
    const leftFixedColumns = props.tableColumns.filter(c => c.fixed === 'left' && c.key && c.computedWidth);
    const rightFixedColumns = props.tableColumns.filter(c => c.fixed === 'right' && c.key && c.computedWidth);

    leftFixedWidthSum.value = leftFixedColumns.reduce((sum, col) => {
        return sum + col.computedWidth!;
    }, 0);

    if(props.isCheckBox){
        leftFixedWidthSum.value += 30;
    }

    rightFixedWidthSum.value = rightFixedColumns.reduce((sum, col) => {
        return sum + col.computedWidth!;
    }, 0);    
};
// 新增响应式状态
const scrollState = reactive({
    isAtLeft: true,
    isAtRight: false
});

const checkScrollPosition = (element: HTMLElement) => {
    const { scrollLeft, scrollWidth, clientWidth } = element;
    const epsilon = 1; // 容错值，避免精度问题    
    
    // 判断是否在最左侧
    scrollState.isAtLeft = scrollLeft <= epsilon;
    
    // 判断是否在最右侧
    scrollState.isAtRight = scrollLeft + clientWidth >= scrollWidth - epsilon;    
};

// 初始化和更新滚动处理器
// 添加缓存变量
const scrollbarWidth = ref(0);
let resizeObserver: ResizeObserver;
onMounted(() => {
    if (bodyWrapper.value) {
        // 初始化时计算可见数据起始和结束索引
        const visibleItemCount = Math.ceil(bodyWrapper.value.clientHeight / itemHeight);
        state.visibleEndIndex = visibleItemCount + bufferSize;
        //动态调整 header 宽度
        resizeObserver = new ResizeObserver(calcScrollbarWidth);
        resizeObserver.observe(bodyWrapper.value);  
    }
});

onUnmounted(() => {
    if (resizeObserver) resizeObserver.disconnect();
    handleScroll.cancel();
});

// 获得所有选中的行数据集合
const getSelectedRows = (): RowData[] => {
    if (!selectedMap.value || !props.data) {
        return [];
    }
    const selectedRows: RowData[] = [];
    Array.from(selectedMap.value).forEach((index: number) => {
        if (index >= 0 && index < props.data.length) {
            selectedRows.push(props.data[index]);
        }
    });
    return selectedRows;
};
// 计算合计数据
const totalData = computed(() => {
    const sums: any[] = [];
    props.tableColumns.forEach((column, index) => {
        if (index === 0) {
            sums.push('合计');
        } else if (column.key === 'dealAmount' || column.key === 'enBalance') {
            const sum = Array.from(selectedMap.value).reduce((prev: number, currIndex: number) => {
                const rowData = props.data[currIndex];
                return prev + (Number(rowData[column.key]) || 0);
            }, 0);
            sums.push(sum);
        } else {
            sums.push('');
        }
    });
    return sums;
});
// 监听data变化更新滚动
// watch(data.value, () => {
//     handleScroll({ target: bodyWrapper.value });
// });
// 新增tooltips
interface TooltipState {
    isShow: boolean;
    isTips: boolean;
    position: { x: number; y: number }
}
const tooltipState = ref<Record<string, TooltipState>>({})
const handleCellEnter = (event: MouseEvent, key: string) => {
    const target = event.currentTarget as HTMLElement;    
    // 判断内容是否溢出
    const isOverflow = target.scrollWidth > target.clientWidth || target.scrollHeight > target.clientHeight;
    if(isOverflow){
        const rect = target.getBoundingClientRect();   
        tooltipState.value[key] = {
            isShow: isOverflow,
            isTips: false,
            position: {
                x: rect.left,
                y: window.innerHeight - rect.top
            }
        };
    }    
};

const handleCellLeave = (key: string) => {
    if (tooltipState.value[key]) {
        tooltipState.value[key].isShow = false;
    }
};
const handleTipsEnter = (key: string) => {
    if (tooltipState.value[key]) {
        tooltipState.value[key].isTips = true;
    }
};

const handleTipsLeave = (key: string) => {
    if (tooltipState.value[key]) {
        tooltipState.value[key].isTips = false;
    }
};
// 激活组件时恢复位置
onActivated(() => {
    if (bodyWrapper.value) {
        bodyWrapper.value.scrollTop = lastScrollTop;
        bodyWrapper.value.scrollLeft = lastScrollLeft;        
    }
});
defineExpose({
    getSelectedRows
});
</script>

<style scoped lang="scss">
@use './virtualTable.scss';
</style>